/* Chibi IT Play  - Copyright (c) 2009, Juan Linietsky */
/*

License:

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/



#ifndef CHIBI_IT_PLAY_H
#define CHIBI_IT_PLAY_H

#ifdef IT_CPU16


#else

typedef unsigned char it_u8;
typedef unsigned short it_u16;
typedef unsigned int it_u32;

typedef signed char it_s8;
typedef signed short it_s16;
typedef signed int it_s32;

/* override with wathever makes your compiler happy */

#endif

typedef it_u8 it_bool;
#define it_true 1
#define it_false 0

/** INSTANCE **/

typedef struct {	

	void *_private;
} IT_Instance;
/** MIXER **/

typedef enum {

	IT_CONSTANT_MAX_NOTES=120,
	IT_CONSTANT_MAX_PATTERNS=255,
	IT_CONSTANT_MAX_INSTRUMENTS=99,
	IT_CONSTANT_MAX_SAMPLES=256, /* support XM and modplug */
	IT_CONSTANT_MAX_ENVELOPE_POINTS=25,
	IT_CONSTANT_MAX_ORDERS=256,

	IT_CONSTANT_MAX_FADEOUT=256,

	IT_CONSTANT_NOTE_CUT=254,
	IT_CONSTANT_NOTE_OFF=255,

	IT_CONSTANT_ORDER_SKIP=256,
	IT_CONSTANT_ORDER_EMPTY=257,
	
	IT_CONSTANT_MAX_LOADER_FUNCS=32,

	IT_INVALID_SAMPLE_ID=-1,
	IT_CONSTANT_MAX_CHANNELS=64,

	IT_CONSTANT_DEFAULT_MAX_VOICES=128
} IT_Constants;

/** AUDIO LOCK **/


typedef struct {

	void (*lock)(); /** Lock the Mixer */
	void (*unlock)(); /** Lock the Mixer */

} IT_AudioLock;

/** MIXER **/

typedef enum {

	IT_SAMPLE_FORMAT_PCM8, /* signed 8-bits */
	IT_SAMPLE_FORMAT_PCM16, /* signed 16-bits */
	IT_SAMPLE_STEREO_FLAG=(1<<7),
	IT_SAMPLE_FORMAT_MASK=IT_SAMPLE_STEREO_FLAG-1
	
} IT_SampleFormat;


typedef enum {

	IT_LOOP_DISABLED,
	IT_LOOP_FORWARD,
	IT_LOOP_PING_PONG
} IT_LoopType;

typedef struct {

	IT_SampleFormat format;
	IT_LoopType loop_type;
	it_u32 loop_begin;  /* position in audio frames */
	it_u32 loop_end;  /* position in audio frames */
	IT_LoopType sustain_loop_type;
	it_u32 sustain_loop_begin;  /* position in audio frames */
	it_u32 sustain_loop_end;  /* position in audio frames */
	it_u32 length; /* size in audio frames */
		
} IT_SampleData;

typedef it_s32 IT_SampleID;

/** IT_Mixer:
  *
  * Chibi IT Play provides a default software mixer, but for portability issues, or taking advantage of
  * a certain architecture or mixing hardware, the mixer can be reimplemented for any other backend.
  *
  * Methods tagged with *LOCK* mean that they must not be called directly by the user,
  * unless locking/unlocking is performed before/after calling them, or calling from the same
  * thread as the process callback.
  */

typedef struct _IT_Mixer {

	void (*set_process_callback)(struct _IT_Mixer* p_mixer, void (*p_process_callback)(void*), void* p_userdata); /** callback to use for every interval. This is called from the sound thread (used by player, don't call) */
	void (*set_process_callback_interval)(struct _IT_Mixer* p_mixer,it_u32 p_usec); /** set interval for process callback, in usecs (used by player, don't call) */

	void (*voice_start)(struct _IT_Mixer* p_mixer,it_u8 p_voice,IT_SampleID p_sample,it_u32 p_offset); /** start offset in audio frames, *LOCK* */
	void (*voice_stop)(struct _IT_Mixer* p_mixer,it_u8 p_voice); /** stop voice, *LOCK* */
	void (*voice_set_volume)(struct _IT_Mixer* p_mixer,it_u8 p_voice,it_u8 p_vol); /** volume from 0 to 255 *LOCK* */
	void (*voice_set_pan)(struct _IT_Mixer* p_mixer,it_u8 p_voice,it_u8 p_pan); /** pan from 0 to 255, 127 is center *LOCK* */
	void (*voice_set_speed)(struct _IT_Mixer* p_mixer,it_u8 p_voice,it_u32 p_hz); /** speed, in audio frames per second *LOCK* */
	void (*voice_set_filter)(struct _IT_Mixer* p_mixer,it_u8 p_voice,it_u32 p_cutoff_hz,it_u8 p_resonance); /** speed, in audio frames per second *LOCK* */

	it_bool (*voice_is_active)(struct _IT_Mixer* p_mixer,it_u8 p_voice); /** speed, in audio frames/second *LOCK* */

	IT_SampleID (*sample_create)(struct _IT_Mixer* p_mixer,const IT_SampleData *p_sample_data); /** Mixer creates empty sample */
	void (*sample_set_frame)(struct _IT_Mixer* p_mixer,IT_SampleID p_sample,it_u32 p_frame, it_u8 p_channel,it_s16 p_value); /** Mixer receives data */
	void (*sample_free)(struct _IT_Mixer* p_mixer,IT_SampleID p_sample); /** Mixer releases ownership of the sample, and may free the sample data */
	void (*reset_voices)(struct _IT_Mixer* p_mixer); /** silence and reset all voices */
	void (*reset_samples)(struct _IT_Mixer* p_mixer); /** unregister all samples */

	it_u16 (*get_voice_count)(struct _IT_Mixer* p_mixer);

} IT_Mixer;

IT_Mixer *it_instance_get_mixer(IT_Instance *p_instance);

/** SAMPLE **/

typedef enum {
	IT_VIBRATO_SINE,
	IT_VIBRATO_SAW,
	IT_VIBRATO_SQUARE,
	IT_VIBRATO_RANDOM

} IT_VibratoType;

typedef struct {

	IT_SampleID sample_id;
	it_u8 default_volume;
	it_u8 global_volume;
	it_s8 pan; /* -32.. 32 */
	it_bool pan_enabled;
	
	IT_VibratoType vibrato_type;
	it_u8 vibrato_speed; /* 0.. 64 */
	it_u8 vibrato_depth; /* 0.. 64 */
	it_u8 vibrato_rate; /* 0.. 64 */

	it_u32 c5_hz; /* base sample rate */

} IT_Sample;

/** INSTRUMENT **/

typedef enum {

	IT_ENVELOPE_ENABLED=(1<<0), /** If Loop is enabled, Enable Sustain */
	IT_ENVELOPE_LOOP_ENABLED=(1<<1), /** Loop is enabled **/
	IT_ENVELOPE_SUSTAIN_ENABLED=(1<<2), /** If Loop is enabled, Enable Sustain */
	IT_ENVELOPE_CARRY_ENABLED=(1<<3), /** Envelope Carry */
	IT_ENVELOPE_IS_FILTER=(1<<4) /** Envelope is filter (for filter envelope) */

} IT_EnvelopeFlags;


typedef struct {
	
	it_u16 offset;
	it_s8 value;
	
} IT_EnvelopePoint;


typedef struct {

	IT_EnvelopePoint points[IT_CONSTANT_MAX_ENVELOPE_POINTS]; /* Envelope points */
	it_u8 point_count;
	it_u8 flags; /* Envelope Flags & Point count ( IT_EnvelopeFlags ) */
	
	it_u8 loop_begin_index; /* Loop Begin Point Index */
	it_u8 loop_end_index; /* Loop End  Point Index */
	it_u8 sustain_loop_begin_index; /* Loop Begin Point Index */
	it_u8 sustain_loop_end_index; /* Loop End  Point Index */

} IT_Envelope;

typedef enum {

	IT_NNA_NOTE_CUT,
	IT_NNA_NOTE_CONTINUE,
	IT_NNA_NOTE_OFF,
	IT_NNA_NOTE_FADE
} IT_NNA_Type; /* new note action */

typedef enum {

	IT_DCT_DISABLED,
	IT_DCT_NOTE,
	IT_DCT_SAMPLE,
	IT_DCT_INSTRUMENT
} IT_DC_Type;

typedef enum {

	IT_DCA_NOTE_CUT,
	IT_DCA_NOTE_OFF,
	IT_DCA_NOTE_FADE
} IT_DC_Action;

typedef struct {

	/* Envelopes */

	IT_Envelope volume_envelope;
	IT_Envelope pan_envelope;
	IT_Envelope pitch_filter_envelope;

	IT_NNA_Type nna_type;
	IT_DC_Type dc_type;
	IT_DC_Action dc_action;

	it_u8 global_volume;
	it_u16 fadeout;
	it_u8 random_volume;

	it_u8 pan;
	it_bool pan_enabled;
	it_s8 pan_pitch_separation;
	it_u8 pan_pitch_center;
	it_u8 random_pan;
	
	it_bool filter_cutoff_enabled;
	it_u8 filter_cutoff;
	it_bool filter_resonance_enabled;
	it_u8 filter_resonance;

	it_u8 note_sample[IT_CONSTANT_MAX_NOTES]; /* sample for each note */
	it_u8 note_note[IT_CONSTANT_MAX_NOTES]; /* note for each note */

} IT_Instrument;



/** SONG **/

typedef enum {
	IT_SONG_FLAG_LINEAR_PERIODS=1<<0,
	IT_SONG_FLAG_OLD_EFFECTS=1<<1,
	IT_SONG_FLAG_STEREO=1<<2,
	IT_SONG_FLAG_COMPATIBLE_GXX=1<<3,
	IT_SONG_FLAG_USE_INSTRUMENTS=1<<4
} IT_SongFlags;

typedef enum {
	IT_CHANNEL_FLAG_MUTE=1<<0,
	IT_CHANNEL_FLAG_SURROUND=1<<1
} IT_ChannelFlags;

typedef struct {

	it_u8 flags;
	it_s8 pan; /* -32 .. 32 */
	it_u8 volume; /* 0 .. 64 */
	it_u8 chorus; /* 0 .. 64 */
	it_u8 reverb; /* 0 .. 64 */
	
} IT_ChannelDefaults;


typedef enum {
	IT_REVERB_MODE_DISABLED,
	IT_REVERB_MODE_ROOM,
	IT_REVERB_MODE_STUDIO_SMALL,
	IT_REVERB_MODE_STUDIO_MEDIUM,
	IT_REVERB_MODE_STUDIO_LARGE,
	IT_REVERB_MODE_HALL,
	IT_REVERB_MODE_SPACE_ECHO,
	IT_REVERB_MODE_ECHO,
	IT_REVERB_MODE_DELAY,
	IT_REVERB_MODE_HALF_ECHO
} IT_ReverbMode;

/* SONG HEADER, size in mem: 272 bytes */

typedef struct {

	it_u16 rows;
	it_u16 data_size;
	it_u8 data[1];
} IT_Pattern;

typedef struct {

	char name[26];
	char *message;
	
	it_u8 restart_pos; /* used in XM */
	it_u8 flags; /* flags, defined in SongFlags (including channels used) */
	
	it_u8 tempo;
	it_u8 speed;
	it_u8 global_volume;
	it_u8 mixing_volume; /* ignored by player */

	it_u8 row_hl_min; /* ignored by player */
	it_u8 row_hl_maj; /* ignored by player */

	IT_ReverbMode reverb_mode; /* ChibiTracker compatibility */

	IT_ChannelDefaults channels[IT_CONSTANT_MAX_CHANNELS];
	it_u8 channel_count;

	IT_Pattern* patterns[IT_CONSTANT_MAX_PATTERNS];
	IT_Sample *samples[IT_CONSTANT_MAX_SAMPLES];
	IT_Instrument *instruments[IT_CONSTANT_MAX_INSTRUMENTS];
	it_u16 order_list[256];

} IT_Song;

IT_Song *it_song_create(IT_Instance *p_instance);
void it_song_free_music( IT_Instance *p_instance, IT_Song *p_song ); /** Free patterns, p_song is NOT freed, use it_song_free */
void it_song_free_instruments( IT_Instance *p_instance, IT_Song *p_song ); /** free instruments/samples */
void it_song_free(IT_Instance *p_instance,IT_Song *p_song);

/** PLAYER **/

typedef struct {
	void *_private;	
} IT_Player;

IT_Player* it_player_create(IT_Instance *p_instance);
void it_player_free(IT_Player *p_player);

void it_player_set_song(IT_Player *p_player, IT_Song *p_song);

void it_player_play(IT_Player *p_player);
void it_player_pause(IT_Player *p_player);
void it_player_stop(IT_Player *p_player);

/** FILEIO **/

typedef enum {

	IT_FILE_OK,
	IT_FILE_ERROR_CANT_OPEN,
	IT_FILE_ERROR_IN_USE
} IT_FileIOError;

typedef struct {
	it_bool (*eof_reached)(); /** Returns true if EOF */
	it_bool (*in_use)(); /** FileIO is working with a file */
	IT_FileIOError (*open)(const char *p_file,it_bool p_big_endian_mode); /** Open a File **/
	it_u8  (*get_u8)(); /** Get byte from file */
	it_u16 (*get_u16)(); /** Get 16 bits word from file **/
	it_u32 (*get_u32)(); /** Get 32 bits dword from file **/
	void   (*get_u8_array)(it_u8 *p_dst,it_u32 p_count); /** Get a byte array from the file */
	void   (*seek)(it_u32 p_offset); /** Seek to a file position **/
	it_u32 (*get_pos)(); /** Get the current file position **/
	void(*close)(); /** Close the file **/
} IT_FileIO;

/** MEM-IO **/

/* Each type of allocation is detailed by the loader, this way,
   the host implementation can manage the optimum memory allocation scheme
   for music if necesary */

typedef enum {
	IT_MEMORY_ALLOC_INSTANCE,
	IT_MEMORY_ALLOC_SONG_HEADER,
	IT_MEMORY_ALLOC_INSTRUMENT,
	IT_MEMORY_ALLOC_SAMPLE,
	IT_MEMORY_ALLOC_SAMPLE_DATA,
	IT_MEMORY_ALLOC_PATTERN,
	IT_MEMORY_ALLOC_MESSAGE,
	IT_MEMORY_ALLOC_AUXILIARY,
	IT_MEMORY_ALLOC_PLAYER, /** Memory needed for IT_Player, allocated once **/
	IT_MEMORY_ALLOC_SOFTMIXER /** Memory needed for SoftMixer **/
} IT_MemoryAllocType;

typedef struct {

	void* (*alloc)(it_u32 p_size,IT_MemoryAllocType p_alloc_type); /* alloc mem */
	void (*free)(void *p_mem,IT_MemoryAllocType p_free_type); /* free mem */

} IT_MemoryManager;

/** LOADER **/

typedef enum {

	IT_LOADER_OK,
	IT_LOADER_UNCONFIGURED, /** FileIO/Mixer/MemoryIO was not set **/
	IT_LOADER_ERROR_FILEIO_IN_USE,
	IT_LOADER_ERROR_FILE_CANT_OPEN,
	IT_LOADER_ERROR_FILE_UNRECOGNIZED,
	IT_LOADER_ERROR_OUT_OF_MEMORY,
	IT_LOADER_ERROR_FILE_CORRUPT
} IT_LoaderError;

typedef enum {
	
	 IT_LOADER_FLAG_LOAD_MUSIC=1<<0,
	 IT_LOADER_FLAG_LOAD_INSTRUMENTS=1<<1,
	 IT_LOADER_FLAGS_DEFAULT=IT_LOADER_FLAG_LOAD_MUSIC|IT_LOADER_FLAG_LOAD_INSTRUMENTS	 
} IT_LoaderFlags;

typedef IT_LoaderError (*IT_LoaderFunc)(IT_Song *p_song,IT_Instance *p_instance,IT_FileIO *p_file_io,IT_MemoryManager *p_memory,const char *p_filename,it_u8 p_loader_flags); /* user-supplied loader for certain types of songs. If format or extension is not recognized, return UNRECOGNIZED */

/** INSTANCE */

IT_LoaderError it_instance_load_song(IT_Instance *p_instance,IT_Song *p_song,const char *p_filename,it_u8 p_loader_flags);

void it_instance_create(IT_Instance *r_instance, IT_MemoryManager *p_memory, IT_FileIO *p_file_io, IT_Mixer *p_mixer, IT_AudioLock *p_audio_lock, IT_LoaderFunc *p_loaders);
void it_instance_free(IT_Instance *p_instance);

/** SOFTMIXER */

IT_Mixer* it_softmixer_create(int p_mix_rate,int p_max_voices,IT_MemoryManager *p_memory);
it_u32 it_softmixer_mix(IT_Mixer *p_mixer,it_s32 *p_buffer,it_u32 p_frames); /** mixes in stereo interleaved 32-bits */
void it_softmixer_free(IT_Mixer *p_mixer);


#endif
